home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
dev
/
e
/
amigae21b.lha
/
Amiga_E_v2.1b
/
Docs
/
Reference.doc
< prev
next >
Wrap
Text File
|
1992-09-02
|
98KB
|
2,746 lines
+-----------------------------------------------+
| |
| Amiga E v2.1b |
| Compiler for The E Language |
| By Wouter van Oortmerssen |
| |
| Language Reference |
| |
+-----------------------------------------------+
Contents:
1. format
A. tabs,lf etc.
B. comments
C. identifiers and types
2. immediate values
A. decimal (1)
B. hexadecimal ($1)
C. binary (%1)
D. float (1.0)
E. character ("a")
F. strings ('bla')
G. lists ([1,2,3]) and typed lists
3. expressions
A. format
B. precedence and grouping
C. types of expressions
D. function calls
4. operators
A. math (+ - * /)
B. comparison (= <> > < >= <=)
C. logical and bitwise (AND OR)
D. unary (SIZEOF ` ^ {} ++ -- -)
E. triple (IF THEN ELSE)
F. structure (.)
G. array ([])
H. float operator (|)
I. assignments expressions (:=)
J. sequencing (BUT)
5. statements
A. format (;)
B. statement labels and gotos (JUMP)
C. assignment (:=)
D. assembly mnemonics
E. conditional statement (IF)
F. for-statement (FOR)
G. while-statement (WHILE)
H. repeat-statement (REPEAT)
I. loop-statement (LOOP)
J. select-case-statement (SELECT)
K. increase statement (INC/DEC)
L. void expressions (VOID)
6. function definitions and declarations
A. proc definition and arguments (PROC)
B. local and global definitions: scope (DEF)
C. endproc/return
D. the "main" function
E. built-in system variables
7. declaration of constants
A. const (CONST)
B. enumerations (ENUM)
C. sets (SET)
D. built-in constants
8. types
A. about the "type" system
B. the basic type (LONG/PTR)
C. the simple type (CHAR/INT/LONG)
D. the array type (ARRAY)
E. the complex type (STRING/LIST)
F. the compound type (OBJECT)
G. initialisation
9. built-in functions
A. io functions
B. strings and string functions
C. lists and list functions
D. intuition support functions
E. graphics support functions
F. system support functions
G. math and other functions
H. string and list linking functions
10. library functions and modules
A. built-in library calls
B. interfacing to the amiga system with the 2.04 modules
11. quoted expressions
A. quoting and scope
B. Eval()
C. built-in functions
12. floating point support
A. using floats and float operator overloading
B. float expressions and conversion
13. Exception handling
A. defining exception handlers (HANDLE/EXCEPT)
B. using the Raise() function
C. defining exceptions for built-in functions (RAISE/IF)
D. use of exception-ID's
14. OO programming
15. inline assembly
A. identifier sharing
B. the inline assembler compared to a macro assembler
C. ways using binary data (INCBIN/CHAR..)
D. OPT ASM
16. implementation issues
A. the OPT keyword
B. small/large model
C. stack organisation
D. hardcoded limits
E. error messages, warnings and the unreferenced check
F. compiler buffer organisation and allocation
G. a brief history
+---------------------------------------------------------------+
| 1. FORMAT |
+---------------------------------------------------------------+
1A. tabs,lf etc.
----------------
E-sources are pure ascii format files, with the linefeed <lf> and
semicolon ";" being the separators for two statements. Statements
that have particulary many arguments, separated by commas ",", may
be spread over several lines by ending a line with a comma, thus
ignoring the following <lf>.
Any lexical element in a source code file may be separated from another
by any number of spaces, tabs etc.
1B. comments
------------
comments may be placed anywhere in a source file where normally a space
would have been correct. They start with '/*' and end with '*/'
and may be nested infinitely.
1C. identifiers and types
-------------------------
identifiers are strings that the programmer uses to denote certain
objects, in most cases variables, or even keywords or function names
predefined by the compiler. An identifier may consist of:
- upper and lowercase characters
- "0" .. "9" (except as first character)
- "_" (the underscore)
All characters are significant, but the compiler just looks at the
first two to identify the type of identifier it is dealing with:
both uppercase: - keyword like IF, PROC etc.
- constant, like MAX_LENGTH
- assembly mnemonic, like MOVE
first lowercase: - identifier of variable/label/object etc.
first upper and second lower: - E system function like: WriteF()
- library call: OpenWindow()
Note that all identifiers obey this syntax, for example:
WBenchToFront() becomes WbenchToFront()
+---------------------------------------------------------------+
| 2. IMMEDIATE VALUES |
+---------------------------------------------------------------+
Immediate values in E all evaluate to a 32bit result; the only
difference among these values (A-G) is either their internal
representation, or the fact that they return a pointer rather than
a value.
2A. decimal (1)
---------------
A decimal value is a sequence of characters "0" .. "9", possibly
preceded by a minus "-" sign to denote negative numbers.
examples: 1, 100, -12, 1024
2B. hexadecimal ($1)
--------------------
A hexadecimal value uses the additional characters "A" .. "F" (or
"a" .. "f") and is preceded by a "$" character.
Examples: $FC, $DFF180, -$ABCD
2C. binary (%1)
---------------
Binary numbers start with a "%" character and use only "1" and "0"
to form a value.
Examples: %111, %1010100001, -%10101
2D. float (1.0)
---------------
Floats differ only from normal decimal numbers in that they have
a "." to separate their two parts. Either one may be omitted,
not both. Note that floats have a different internal 32bit (FFP)
representation. See chapter 12 for more information on floats.
Examples: 3.14159, .1 (=0.1), 1. (=1.0)
2E. character ("a")
-------------------
The value of a character (enclosed in double "" quotes) is their
ascii value, i.e. "A" = 65. In E, character immediate values
may be a short string up to 4 characters, for example "FORM",
where the first character "F" will be the MSB of the 32bit
representation, and "M" the LSB (least significant byte).
2F. string ('bla')
------------------
Strings are any ascii representation, enclosed in single '' quotes.
The value of such a string is a pointer to the first character of it.
More specific: 'bla' yields a 32bit pointer to a memory area where
we find the bytes "b", "l" and "a". ALL strings in E are terminated
by a zero 0 byte.
Strings may contain format signs introduced by a slash "\", either
to introduce characters to the string that are for some reason
not displayable, or for use with string formatting functions
like WriteF(), TextF() and StringF(), or kick2 Vprintf().
\n a linefeed (ascii 10)
\a or '' an apostrophe ' (the one used for enclosing the string)
\e escape (ascii 27)
\t tab (ascii 9)
\\ a backslash
\0 a zero byte. Of rare use, as ALL strings are 0-terminated
\b a carriage return (ascii 13)
Additionally, when used with formatting functions:
\d print a decimal number
\h print a hexadecimal
\s print a string
\c print a character
\z set fill byte to '0' character
\l format to left of field
\r format to right of field
Field specifiers may follow the \d,\h and \s codes:
[x] specify exact field width x
(x,y) specify minimum x and maximum y (strings only)
Example: print a hexadecimal number with 8 positions and leading zeroes:
WriteF('\z\h[8]\n',num)
A string may be extended over several lines by trailing them with a "+"
sign and a <lf>:
'this specifically long string ' +
'is separated over two lines'
2G. lists ([1,2,3]) and typed lists
-----------------------------------
An immediate list is the constant counterpart of the LIST datatype,
just as a 'string' is the constant counterpart for the STRING or
ARRAY OF CHAR datatype. Example:
[3,2,1,4]
is an expression that has as value a PTR to an already initialised list,
a list as a representation in memory is compatible with an ARRAY OF LONG,
with some extra length information at a negative offset. You may use these
immediate lists anywhere a function expects a PTR to an array of
32bits values, or a list. Examples:
['string',1.0,2.1]
[WA_FLAGS,1,WA_IDCMP,$200,WA_WIDTH,120,WA_HEIGHT,150,TAG_DONE]
See the part on list-functions for a discussion on typed-immediate
lists and detailed information.
+---------------------------------------------------------------+
| 3. EXPRESSIONS |
+---------------------------------------------------------------+
3A. format
----------
An expression is a piece of code held together by operators, functions
and brackets to form a value.
They mostly consist of:
- immediate values as discussed in chapter 2
- operators as discussed in chapter 4
- function calls as discussed in chapter 3D
- brackets () as discussed in chapter 3B
- variables or variable-expressions (see 3C)
examples of expressions:
1
'hello'
$ABCD+(2*6)+Abs(a)
(a<1) OR (b>=100)
3B. precedence and grouping
---------------------------
The E language has _no_ precedence whatsoever. This means that
expressions are evaluated left to right. You may change
precedence by bracketing some (sub-)expression:
1+2*3 /* =9 */ 1+(2*3) /* =7 */ 2*3+1 /* =7 */
3C. types of expressions
------------------------
There are three types of expressions that may be used for different
purposes;
- <var>, consisting of just a variable
- <varexp>, consisting of a variable, possibly with unary operators
with it, like ++ (increment) or [] (array operator). For those,
see chapter 4D and 4G. It denotes a modifiable expression, like
Lvalues in C.
Note that those (unary) operators are not part of any precedence.
- <exp>. This includes <var> and <varexp>, and any other expression.
3D. function calls
------------------
A function call is a temporary suspension of the current code for a
jump to a function, this may be either a self-written function (PROC),
or a function provided by the system. The format of a function call
is the name of the function, followed by two brackets () enclosing
zero to unlimited arguments, separated by commas ",".
Note that arguments to functions are again expressions.
See chapter 6 on how to make your own functions, and chapters 9 and 10
on built-in functions. Examples:
foo(1,2)
Gadget(buffer,glist,2,0,40,80+offset,100,'Cancel')
Close(handle)
+---------------------------------------------------------------+
| 4. OPERATORS |
+---------------------------------------------------------------+
4A. math (+ - * /)
------------------
These infix operators combine an expression with another value
to yield a new value. Examples:
1+2, MAX-1*5
see chapter 12 on how to overload these operators for use with floats.
"-" may be used as the first part of an expression, with an implied 0,
i.e. -a or -b+1 is legal.
Note that * and / are by default 16bit operators: see Mul()
4B. comparison (= <> > < >= <=)
-------------------------------
Equal to math operators, with the difference that they result in either
TRUE (32bit value -1), or FALSE. These can also be overloaded for floats.
4C. logical and bitwise (AND OR)
--------------------------------
These operators either combine truth values to new ones, or perform
bitwise AND and OR operations. Examples:
(a>1) AND ((b=2) OR (c>=3)) /* logical */
a:=b AND $FF /* bitwise */
4D. unary (SIZEOF ^ {} ++ -- `)
---------------------------------
- SIZEOF <objectident>
simply returns the size of a certain object.
Example: SIZEOF newscreen
- {<var>}
Returns the address of a variable or label. This is the operator
you would use to give a variable as argument to a function by
reference, instead of by value, which is default in E. See "^".
Example: Val(input,{x})
- ^<var>
The counterpart of {}, writes or reads variables that were given by
reference, examples: ^a:=1 b:=^a
it may also be used to plainly "peek" or "poke" LONG values from
memory, if <var> is pointer to such a value.
Example for {} and ^: write your own assignment function;
PROC set(var,exp)
^var:=exp
ENDPROC
and call it with: set({a},1) /* equals a:=1 */
- <varexp>++ and <varexp>--
Increases (++) or decreases (--) the pointer that is denoted by
<varexp> by the size of the data it points to. This has the effect
that that pointer points to the next or previous item. When used
on variables that are not pointers, these will simply be changed
by one. Note that ++ always takes effect _after_ the calculation
of <varexp>, -- always _before_. Examples:
a++ /* return value of a, then increase by one */
sp[]-- /* decrease pointer sp by 4 (if it were an array of long),
and read value pointed at by sp */
- `<exp>
This is called a quoted expression, from LISP. <exp> is not evaluated,
but instead returns the address of the expression, which can later be
evaluated when required. More on this special feature in chapter 11
4E. triple (IF THEN ELSE)
-------------------------
The IF operator has quite the same function as the IF statement, only
it selects between two expressions instead of two statements or blocks
of statements. It equals the x?y:z operator in C.
IF <boolexp> THEN <exp1> ELSE <exp2>
returns exp1 or exp2, according to boolexp. For example, instead of:
IF a<1 THEN b:=2 ELSE b:=3
IF x=3 THEN WriteF('x is 3\n') ELSE WriteF('x is something else\n')
write:
b:=IF a<1 THEN 2 ELSE 3
WriteF(IF x=3 THEN 'x is 3\n' ELSE 'x is something else\n')
4F. structure (.)
-----------------
<ptr2object>.<memberofobject> builds a <varexp>
The pointer has to be declared as PTR TO <object> or ARRAY OF <object>
(see chapter 8 for these), and the member has to be a legal
object identifier. Note that reading a subobject in an object this
way results in a pointer to that object. Examples:
thistask.userdata:=1
rast:=myscreen.rastport
4G. array ([])
--------------
<var>[<indexexp>] (is a <varexp>)
This operator reads the value from the array <var> points to, with
index <indexexp>. The index may be just about any expression, with
little limitations that it should not contain function calls and the like
when used on the left-hand side of an assignment.
Note1: "[]" is a shortcut for "[0]"
Note2: with an array of n elements, the index may be 0 .. n-1
Examples:
a[1]:=10 /* sets second element to 10 */
x:=table[y*4+1] /* reads from array */
4H. float operator (|)
----------------------
<exp>|<exp>
Converts expressions from integer to float and back, and
overloads operators + - * / = <> < > <= >= with float equivalents.
See chapter 12 to read all about floats and this operator.
4I. assignments expressions (:=)
--------------------------------
Assignments (setting a variable to a value) exist as statement
and as expression. The only difference is that the statement
version has the form <varexp>:=<exp> and the expression <var>:=<exp>.
The latter has the value of <exp> as result.
Note that as <var>:= takes on an expression, you will often need
parentheses to force correct interpretation, like:
IF mem:=New(100)=NIL THEN error()
is interpreted like:
IF mem:=(New(100)=NIL) THEN error()
which is not what you mean: mem should be a pointer, not a boolean.
but you should write:
IF (mem:=New(100))=NIL THEN error()
4J. sequencing (BUT)
--------------------
The sequencing operator "BUT" allows two expressions to be written
in a construction that allows for only one. Often in writing
complex expressions/function calls, one would like to do a second
thing on the spot, like an assignment. Syntax:
<exp1> BUT <exp1>
this says: evaluate exp1, but return the value of exp2.
Example:
myfunc((x:=2) BUT x*x)
assign 2 to x and then calls myfunc with x*x. The () around the
assignment are again needed to prevent the := operator from taking
(2 BUT x*x) as an expression.
+---------------------------------------------------------------+
| 5. STATEMENTS |
+---------------------------------------------------------------+
5A. format (;)
--------------
As suggested in chapter 1A, a statement generally stands in its
own line, but several of them may be put together on one line
by separating them with semicolon, or one may be spread over
more than one line by ending each line in a comma ",". Examples:
a:=1; WriteF('hello!\n')
DEF a,b,c,d, /* too many args for one line (faked) */
e,f,g
statements may be:
- assignments
- conditional statements, for statements and the like, see chapters 5E-5K
- void expressions
- labels
- assembly instructions
The comma is the primary character to show that you do not wish to
end the statement with the next linefeed, but the following characters
also signal a continuation of a statement on the next line:
+ - * /
= > < <> >= <=
AND OR BUT THEN
5B. statement labels and gotos (JUMP)
-------------------------------------
Labels are global-scoped identifiers with a ':' added to it, as in:
mylabel:
they may be used by instructions such as JUMP, and to reference
static data. They may be used to jump out of all types of loops (although
this technique is not encouraged), but not out of procedures.
In normal E programs they are mostly used with inline assembly.
Labels are always globally visible.
Usage of JUMP: JUMP <label>
continues execution at <label>. You are not encouraged to use this
instruction, it's there for situations that would otherwise increase
the complexity of the program. Example:
IF Mouse()=1 THEN JUMP stopnow
/* other parts of program */
stopnow:
5C. assignment (:=)
-------------------
The basic format of an assignment is: <var> := <exp>
Examples: a:=1, a:=myfunc(), a:=b*3
5D. assembly mnemonics
----------------------
In E, inline assembly is a true part of the language, they need not
be enclosed in special "ASM" blocks or the like, as is usual in
other languages, nor are separate assemblers necessary to assemble
the code. This also means that it obeys the E syntax rules, etc.
See chapter 15 to read all about the inline assembler. Example:
DEF a,b
b:=2
MOVEQ #1,D0 /* just use some assembly statements */
MOVE.L D0,a /* a:=1+b */
ADD.L b,a
WriteF('a=\d\n',a) /* a will be 3 */
5E. conditional statement (IF)
------------------------------
IF, THEN, ELSE, ELSEIF, ENDIF
syntax: IF <exp> THEN <statement> [ ELSE <statement> ]
or: IF <exp>
<statements>
[ ELSEIF <exp> /* multiple elseifs may occur */
<statements> ]
[ ELSE ]
<statements>
ENDIF
builds a conditional block. Note that there are two general forms of
this statement, a single-line and a multiple-line version.
5F. for-statement (FOR)
-----------------------
FOR, TO, STEP, DO, ENDFOR
syntax: FOR <var> := <exp> TO <exp> STEP <step> DO <statement>
or: FOR <var> := <exp> TO <exp> STEP <step>
<statements>
ENDFOR
builds a for-block, note the two general forms. <step> may be
any positive or negative constant, excluding 0. Example:
FOR a:=1 TO 10 DO WriteF('\d\n',a)
5G. while-statement (WHILE)
---------------------------
WHILE, DO, ENDWHILE
syntax: WHILE <exp> DO <statement>
or: WHILE <exp>
<statements>
ENDWHILE
builds a while-loop, which is repeated as long as <exp> is TRUE. Note
the one-line/one-statement version and the multiple line version.
5H. repeat-statement (REPEAT)
-----------------------------
REPEAT, UNTIL
syntax: REPEAT
UNTIL <exp>
builds a repeat-until block: it will continue to loop this block until
<exp>=TRUE. Example:
REPEAT
WriteF('Do you really, really wish to exit this program?\n')
ReadStr(stdout,s)
UNTIL StrCmp(s,'yes please!')
5I. loop-statement (LOOP)
-------------------------
LOOP, ENDLOOP
syntax: LOOP
<statements>
ENDLOOP
builds an infinite loop.
5J. select-case-statement (SELECT)
---------------------------------
SELECT, CASE, DEFAULT, ENDSELECT
syntax: SELECT <var>
[ CASE <exp>
<statements> ]
[ CASE <exp>
<statements> ] /* any number of these blocks */
[ DEFAULT
<statements> ]
ENDSELECT
builds a select-case block. Various expressions will be matched against
the variable, and only the first matching block executed. If nothing matches,
a default block may be executed.
SELECT character
CASE 10
WriteF('Gee, I just found a linefeed\n')
CASE 9
WriteF('Wow, this must be a tab!\n')
DEFAULT
WriteF('Do you know this one: \c ?\n',character)
ENDSELECT
5K. increase statement (INC/DEC)
--------------------------------
INC, DEC
syntax: INC <var>
DEC <var>
short for <var>:=<var>+1 and <var>:=<var>-1. Only difference with
var++ and var-- is that these are statements, and do not return a value,
and are thus more optimal.
5L. void expressions (VOID)
---------------------------
VOID
syntax: VOID <exp>
calculates the expression without the result going anywhere. Only useful
for a clearer syntax, as expressions may be used as statements without
VOID in E anyway. This may cause subtle bugs though, as "a:=1" assigns
a the value 1, but "a=1" as a statement will do nothing. E will give you
a warning if this happens.
+---------------------------------------------------------------+
| 6. FUNCTION DEFINITIONS AND DECLARATIONS |
+---------------------------------------------------------------+
6A. proc definition and arguments (PROC)
----------------------------------------
You may use PROC and ENDPROC to collect statements into your own functions.
Such a function may have any number of arguments, and one return value.
PROC, ENDPROC
syntax: PROC <label> ( <args> , ... )
ENDPROC <returnvalue>
defines a procedure with any number of arguments. Arguments are of type LONG
or optionally of type PTR TO <type> (see chapter 8) and need no further
declaration. The end of a procedure is designated by ENDPROC. If no
return value is given, 0 is returned. Example: write a function that
returns the sum of two arguments:
PROC add(x,y) /* x and y are local variables */
ENDPROC x+y /* return the result */
6B. local and global definitions: scope (DEF)
---------------------------------------------
You may define additional local variables besides those which are
arguments with the DEF statement. The easiest way is simply like:
DEF a,b,c
declares the identifiers a, b and c as variables of your function.
Note that such declarations should be at the start of your function.
DEF
syntax: DEF <declarations>,...
description: declares variables. A declaration has one of the forms:
<var>
<var>:<type> where <type>=LONG,<objectident>
<var>[<size>]:<type> where <type>=ARRAY,STRING,LIST
See chapter 8 for more examples, as that is where the types are introduced.
For now, we'll use the <var> form.
Arguments to functions are restricted to basic types; see chapter 8B.
A declaration of a basic type can have an initialisation, in the current
version this must be an integer (not an expression):
DEF a=1,b=2
A program consists of a set of functions, called procedures, PROCs. Each
procedure may have Local variables, and the program as a whole may have
Global variables. At least one procedure should be the PROC main(), as
this is the module where execution begins. A simple program could look like:
DEF a, b /* definition of global vars */
PROC main() /* all functions in random order */
bla(1)
ENDPROC
PROC bla(x)
DEF y,z /* possibly with own local vars */
ENDPROC
To summarize, local definitions are the ones you make at the start of
procedures, and which are only visible within that function. Global
definitions are made before the first PROC, at the start of your
source code, and they are globally visible. Global and local variables
(and of course local variables of two different functions) may have the
same name, local variables always have priority.
6C. endproc/return
------------------
As stated before, ENDPROC marks the end of a function definition, and may
return a value. Optionally RETURN may be used at any point in the function
to exit, if used in main(), it will exit the program. See also CleanUp()
in chapter 9F.
RETURN [<returnvalue>] /* optional */
Example:
PROC getresources()
/* ... */
IF error THEN RETURN FALSE /* something went wrong, so exit and fail */
/* ... */
ENDPROC TRUE /* we got this far, so return TRUE */
a very short version of a function definition is:
PROC <label> ( <arg> , ... ) RETURN <exp>
These are function definitions that only make small computations, like
faculty functions and the like: (one-liners :-)
PROC fac(n) RETURN IF n=1 THEN 1 ELSE fac(n-1)*n
6D. the "main" function
-----------------------
The PROC called main is only of importance because it is called as first
function; it behaves exactly the same as other functions, and may also
have local variables. Main has no arguments: the command-line arguments
are supplied in the system-variable "arg", or can be checked with
ReadArgs()
6E. built-in system variables
----------------------------
Following global variables are always available in you program,
they're called system variables.
arg As discussed above, contains a pointer to a zero-terminated
string, containing the command-line arguments. Don't use this
variable if you wish to use ReadArgs() instead.
stdout Contains a file-handle to the standard output (and input).
If your program was started from the workbench, so no
shell-output is available, WriteF() will open a
CON: window for you and put its file handle here.
conout This is where that file handle is kept, and the console
window will be automatically closed upon exit of your
program. See WriteF() in section 9A on how to use these
two variables properly.
execbase, These five variables are always provided with their
dosbase, correct values.
gfxbase,
intuitionbase,
mathbase
stdrast Pointer to standard rastport in use with your program,
or NIL. The built-in graphics functions like Line()
make use of this variable.
wbmessage Contains a ptr to a message you got if you started
from wb, else NIL. May be used as a boolean to detect
if you started from workbench, or even check any
arguments that were shift-selected with your icon.
See WbArgs.e in the sources/examples dir how to
make good use of wbmessage.
+---------------------------------------------------------------+
| 7. DECLARATION OF CONSTANTS |
+---------------------------------------------------------------+
7A. const (CONST)
-----------------
syntax: CONST <declarations>,...
Enables you to declare a constant. A declaration looks like:
<ident>=<value>
constants must be uppercase, and will in the rest of the program be
treated as <value>. Example:
CONST MAX_LINES=100, ER_NOMEM=1, ER_NOFILE=2
You cannot declare constansts in terms of others that are being
declared in the same CONST statement: put these in the next.
7B. enumerations (ENUM)
-----------------------
Enumerations are a specific type of constant that need not be given values,
as they simply range from 0 .. n, the first being 0. At any given point
in an enumeration, you may use the '=<value>' notation to set or reset
the counter value. Example:
ENUM ZERO, ONE, TWO, THREE, MONDAY=1, TUESDAY, WEDNESDAY
ENUM ER_NOFILE=100, ER_NOMEM, ER_NOWINDOW
7C. sets (SET)
--------------
Sets are again like enumerations, with the difference that instead of
increasing a value (0,1,2,...) they increase a bitnumber (0,1,2,...) and
thus have values like (1,2,4,8,...). This has the added advantage that
they may be used as sets of flags, as the keyword says.
Suppose a set like the one below to describe properties of a window:
SET SIZEGAD,CLOSEGAD,SCROLLBAR,DEPTH
to initialise a variable to properties DEPTH and SIZEGAD:
winflags:=DEPTH OR SIZEGAD
to set an additional SCROLLBAR flag:
winflags:=winflags OR SCROLLBAR
and to test if two properties hold:
IF winflags AND (SCROLLBAR OR DEPTH) THEN /* ... */
7D. built-in constants
---------------------
Following are built-in constants that may be used:
TRUE,FALSE Represent the boolean values (-1,0)
NIL (=0), the uninitialised pointer.
ALL Used with string functions like StrCopy() to copy all characters
GADGETSIZE Minimum size in bytes to hold one gadget; see Gadget() in 9D
OLDFILE,NEWFILE Mode-parameters for use with Open()
STRLEN Always has the value of the length of the last immediate
string used. Example:
Write(handle,'hi folks!',STRLEN) /* =9 */
+---------------------------------------------------------------+
| 8. TYPES |
+---------------------------------------------------------------+
8A. about the "type" system
---------------------------
E doesn't have a rigid type-system like Pascal or Modula2, it's even more
flexible than C's type system: you might as well call it a datatype-system.
This goes hand in hand with the philosophy that in E all datatypes are
equal: all basic small values like characters, integers etc. All have
the same 32bit size, and all other datatypes like arrays and strings
are represented by 32bit pointers to them. This way, the e compiler can
generate code in a very polymorphic way.
The (dis)advantages are obvious:
disadvantages of the E-type system
- less compiler checking on silly errors you make
advantages:
- low-level polymorphism
- flexible way of programming: no problem that some types of return values
don't match, no superfluous "casts" etc.
- no hard to find errors when mixing data of different sizes in expressions
- still benefit of self-documenting types, if you wish, like:
PTR to newscreen
8B. the basic type (LONG/PTR)
-----------------------------
There's only one basic, non-complex variable type in E, which is the
32bit type LONG. As this is the default type, it may be declared as:
DEF a:LONG or just: DEF a
This variable type may hold what's known as CHAR/INT/PTR/LONG types in other
languages. A special variation of LONG is the PTR type. This type
is compatible with LONG, with the only difference that it specifies
to what type it is a pointer. By default, the type LONG is specified
as PTR TO CHAR. Syntax:
DEF <var>:PTR TO <type>
where type is either a simple type or a compound type. Example:
DEF x:PTR TO INT, myscreen:PTR TO screen
Note that 'screen' is the name of an object as defined in intuition/screens.m
For example, if you open your own screen with:
myscreen:=OpenS(... etc.
you may use the pointer myscreen as in 'myscreen.rastport'. However,
if you do not wish to do anything with the variable until you call
CloseS(myscreen), you may simply declare it as
DEF myscreen
8C. the simple type (CHAR/INT/LONG)
-----------------------------------
The simple types CHAR (8bit) and INT (16bit) may not be used as types
for a basic (single) variable; the reason for this must be clear by now.
However they may be used as data type to build ARRAYs from, set PTRs to,
use in the definition of OBJECTs etc. See those for examples.
8D. the array type (ARRAY)
--------------------------
ARRAYs are declared by specifying their length (in bytes):
DEF b[100]:ARRAY
this defines an array of 100 bytes. Internally, 'b' is a variable of
type LONG and a PTR to this memory area.
Default type of an array-element is CHAR, it may be anything by specifying:
DEF x[100]:ARRAY OF LONG
DEF mymenus[10]:ARRAY OF newmenu
where "newmenu" is an example of a structure, called OBJECTs in E.
Array access is very easy with: <var>[<sexp>]
b[1]:="a"
z:=mymenus[a+1].mutualexclude
Note that the index of an array of size n ranges from 0 to n-1,
and not from 1 to n.
Note that ARRAY OF <type> is compatible with PTR TO <type>, with the
only difference that the variable that is an ARRAY is already
initialised.
8E. the complex type (STRING/LIST)
----------------------------------
- STRINGs. Similar to arrays, but different in the respect that they may
only be changed by using E string functions, and that they contain
length and maxlength information, so string functions may alter them in a
safe fashion, i.e: the string can never grow bigger than the memory
area it is in. Definition:
DEF s[80]:STRING
The STRING datatype is backwards compatible with PTR TO CHAR and
of course ARRAY OF CHAR, but not the other way around.
See the section on string functions for more details.
- LISTs. This is a datatype not found in other procedural languages, it
is something found in languages like ProLog and Lisp. The E version
may be interpreted as a mix between a STRING and an ARRAY OF LONG.
I.e: this data structure holds a list of LONG variables which may be
extended and shortened as STRINGs. Definition:
DEF x[100]:LIST
A powerful addition to this datatype is that it also has a 'constant'
equivalent [], like STRINGs have ''. LIST is backward compatible with
PTR TO LONG and of course ARRAY OF LONG, but not the other way around.
See chapters 2G and 9C for more on this.
8F. the compound type (OBJECT)
------------------------------
OBJECTs are much like a struct in C or a RECORD in pascal. Example:
OBJECT myobj
a:LONG
b:CHAR
c:INT
ENDOBJECT
This defines a data structure consisting of three elements. Syntax:
OBJECT <objname>
<membername> [ : <type> ] /* any number of these */
ENDOBJECT
where type is a simple or again a compound type, or a simple
array type, i.e [<numelements>]:ARRAY with the default CHAR size for
an element. Note that <membername> need not be a unique identifier,
it may be in other objects too. There are lots of ways to use objects:
DEF x:myobj /* x is a structure */
DEF y:PTR TO myobj /* y is just a pointer to it */
DEF z[10]:ARRAY OF myobj
y:=[-1,"a",100]:myobj /* typed lists */
IF y.b="a" THEN /* ... */
z[4].c:=z[d+1].b++
ARRAYs in objects are always rounded to even sizes, and put on
even offsets:
OBJECT mystring
len:CHAR, data[9]:ARRAY
ENDOBJECT
SIZEOF mystring is 12, and "data" starts at offset 2.
NOTE: OBJECTs in E are not like you might be used to in other
languages, for example, not just any type can form a member of
an object, and because of that, recursive object accesses like x.y.z
don't make much sense (yet).
8G. initialisation
------------------
1. Always initialised to NIL (or else, if explicitly stated)
- global variables
NOTE: for documentation purposes, it's always nicer if you
write =NIL in the definitions of variables that you expect to be NIL.
2. Initialised to '' and [] resp.
- global and local STRINGs
- global and local LISTs
3. Not initialised
- local variables (unless explicitly stated)
- global and local ARRAYs
- global and local OBJECTs
+---------------------------------------------------------------+
| 9. BUILT-IN FUNCTIONS |
+---------------------------------------------------------------+
9A. io functions
----------------
WriteF(formatstring,args,...)
prints a string (which may contain formatting codes) to stdout. Zero
to unlimited arguments may be added. Note that, as formatstrings may
be created dynamically, no check on the correct number of arguments
is (can be) made. Examples:
WriteF('Hello, World!\n') /* just write a lf terminated string */
WriteF('a = \d \n',a) /* writes: "a = 123", if a was 123 */
See the bit about strings elsewhere for more.
NOTE: if stdout=NIL, for example if your program was started from the
Workbench, WriteF() will create an output window, and put the handle
in conout and stdout. This window will automatically be closed on
exit of the program, after the user typed a <return>. WriteF() is the
only function that will open this window, so if you want to do IO
on stdout, and want to be sure stdout<>NIL, perform a "WriteF('')"
as first instruction of your program to ensure output. If you want
to open a console window yourself, you may do so by placing the resulting
file handle in the 'stdout' and 'conout' variables, as your window will
then be closed automatically upon exit. If you wish to close this window
manually, make sure to set 'conout' back to NIL, to signal E that there's
no console window to be closed.
Out(filehandle,char) and char:=Inp(filehandle)
Either write or read one single byte to some file or stdout
if char=-1 then an EOF was reached, or an error occurred.
len:=FileLength(namestring)
lets you determine the length of a file you *may* wish to load, and
also, if it exists (returns -1 upon error/file not found).
ok:=ReadStr(filehandle,estring)
see: string support
oldout:=SetStdOut(newstdout)
Sets the standard output variable 'stdout'. Equivalent for:
oldout:=stdout; stdout:=newstdout
9B. strings and string functions
--------------------------------
E has a datatype STRING. This is a string, from now on called 'Estring',
that may be modified and changed in size, as opposed to normal 'strings',
which will be used here for any zero-terminated sequence. Estrings are
downward compatible with strings, but not the other way around, so if an
argument requests a normal string, it can be either of them. If an Estring
is requested, don't use normal strings. Example of usage:
DEF s[80]:STRING, n /* s is an estring with a maxlen of 80 */
ReadStr(stdout,s) /* read input from the console */
n:=Val(s,NIL) /* get a number out of it */
... etc.
Note that all string functions will handle cases where string tends to
get longer than the maximum length correctly;
DEF s[5]:STRING
StrAdd(s,'this string is longer than 5 characters',ALL)
s will contain just 'this '.
A string may also be allocated dynamically from system memory
with the function String(), (note: the pointer returned from this function
must always be checked against NIL)
s:=String(maxlen)
DEF s[80]:STRING is equivalent with DEF s and s:=String(10)
bool:=StrCmp(string,string,len)
compares two strings. len must be the number of bytes to compare,
or 'ALL' if the full length is to be compared. Returns TRUE or FALSE
StrCopy(estring,string,len)
copies the string into the estring. If len=ALL, all will be copied.
StrAdd(estring,string,len)
same as StrCopy(), only now the string is concatenated to the end.
len:=StrLen(string)
calculates the length of any zero-terminated string
len:=EstrLen(estring)
returns the length of an estring
max:=StrMax(estring)
returns the maximum length of a estring
RightStr(estring,estring,n)
fills estring with the last n characters of the second estring
MidStr(estring,string,pos,len)
copies any number of characters (including all if len=ALL) from
position pos in string to estring
NOTEZ BIEN: in all string related functions where a position in a
string is used, the first character in a string has position 0,
not 1, as is common in languages like BASIC.
value:=Val(string,read)
finds an integer encoded in ascii out of a string. Leading spaces/tabs
etc. will be skipped, and also hexadecimal numbers (1234567890ABCDEFabcdef)
and binary numbers (01) may be read this way if they are preceded by a
"$" or a "%" sign respectively. A minus "-" may indicate a negative integer.
Val() returns the number of characters read in the second argument, which
must be given by reference (<-!!!). If "read" returns 0 (value will be 0 too)
then the string did not contain an integer, or the value was too sizy
to fit in 32bit. "read" may be NIL.
examples of strings that would be parsed correctly:
'-12345', '%10101010', ' -$ABcd12'
these would return both as "value" and in var {read} a 0:
'', 'hello!'
foundpos:=InStr(string1,string2,startpos)
searches string1 for the occurrence of string2, possibly starting from
another position than 0. Returned is the *address* at which the substring
was found, else -1.
newstringadr:=TrimStr(string)
returns the *address* of the first character in a string, i.e., after
leading spaces, tabs etc.
UpperStr(string) and LowerStr(string)
changes the case of a string.
TAKE NOTE: these functions modify the contents of 'string', so they may
only be used on estrings, and strings that are part of your programs data.
Effectively this means that if you obtain the address of a string through
some amiga-system function, you must first StrCopy() it to a string of
your program, then use these functions.
ok:=ReadStr(filehandle,estring)
will read a string (ending in ascii 10) from any file or stdout.
ok contains -1 if an error occurred, or an EOF was reached.
Note: the contents of the string read so far is still valid.
SetStr(estring,newlen)
manually sets the length of a string. This is only handy when you read
data into the estring by a function other then an E string function,
and want to continue using it as an Estring. For example, after
using a function that just puts a zero-terminated string at the
address of estring, use SetStr(mystr,StrLen(mystr)) to make
it manipulatable again
for string linking functions see chapter 9H
9C. lists and list functions
----------------------------
Lists are like strings, only they consist of LONGs, not CHARs.
They may also be allocated either global, local or dynamic:
DEF mylist[100]:LIST /* local or global */
DEF a
a:=List(10) /* dynamic */
(note that in the latter case, pointer 'a' may contain NIL)
Just as strings may be represented as constants in expressions, lists
have their constant equivalent:
[1,2,3,4]
The value of such an expression is a pointer to a ready initialised list.
Special feature is that they may have dynamic parts, i.e, which will
be filled in at runtime:
a:=3
[1,2,a,4]
moreover, lists may have some other type than the default LONG, like:
[1,2,3]:INT
[65,66,67,0]:CHAR /* equivalent with 'ABC' */
['topaz.font',8,0,0]:textattr
OpenScreenTagList(NIL,[SA_TITLE,'MyScreen',TAG_DONE])
As shown in the latter examples, lists are extremely useful with
system functions: they are downward compatible with an ARRAY OF LONG,
and object-typed ones can be used wherever a system function needs
a pointer to some structure, or an array of those.
Taglists and vararg functions may also be used this way.
NOTEZ BIEN: all list functions only work with LONG lists, typed-lists
are only convenient in building complex data structures and expressions.
As with strings, a certain hierarchy holds:
list variables -> constant lists -> array of long/ptr to long
When a function needs an array of long you might just as well give a list
as argument, but when a function needs a listvar, or a constant list,
then an array of long won't do.
It's important that one understands the power of lists and in particular
typed-lists: these can save you lots of trouble when building just
about any data-structure. Try to use these lists in your own programs,
and see what function they have in the example-programs. once you get
to grips with lists, you'll never want to write a program without them.
summary:
[<item>,<item>,... ] immediate list (of LONGs, use with listfuncs)
[<item>,<item>,... ]:<type> typed list (just to build data structures)
If <type> is a simple type like INT or CHAR, you'll just have the
initialised equivalent of ARRAY OF <type>, if <type> is an object-name,
you'll be building initialised objects, or ARRAY OF <object>, depending
on the length of the list.
If you write [1,2,3]:INT you'll create a data structure of 6 bytes,
of 3 16bit values to be precise. The value of this expression then
is a pointer to that memory area. Same works if, for example, you have
an object like:
OBJECT myobject
a:LONG, b:CHAR, c:INT
ENDOBJECT
writing [1,2,3]:myobject would then mean creating a data structure
in memory of 8 bytes, with the first four bytes being a LONG with value 1,
the following byte a CHAR with value 2, then a pad byte, and the last
two bytes an INT (2 bytes) with value 3. you could also write:
[1,2,3,4,5,6,7,8,9]:myobject
you would be creating an ARRAY OF myobject with size 3. Note that such
lists don't have to be complete (3,6,9 and so on elements), you may
create partial objects with lists of any size
One last note on data size: on the amiga, you may rely on the fact that
a structure like 'myobject' has size 8, and that it has a pad byte
to have word (16bit) alignment. It is however very likely that an
E-compiler for 80x86 architectures will not use the pad byte and make
it a 7byte structure, and that an E-compiler for a sun-sparc architecture
(if I'm not mistaken) will try to align on 32bit boundaries, thus make
it a 10 or 12 byte structure. Some microprocessors (they are rare, but
they exist) even use (36:18:9) as numbers of bits for their types
(LONG:INT:CHAR), instead of (32:16:8) as we're used to. So don't make too
great an assumption on the structure of OBJECTs and LISTs if you want to
write code that stands a chance of being portable or doesn't rely on side
effects.
ListCopy(listvar,list,num)
Copies num elements from list to listvar. example:
DEF mylist[10]:LIST
ListCopy(mylist,[1,2,3,4,5],ALL)
ListAdd(listvar,list,num)
Copies num items of list to the tail of listvar.
ListCmp(list,list,num)
Compares two lists, or some part of them.
len:=ListLen(list)
Returns length of list, like ListLen([a,b,c]) would return 3
max:=ListMax(listvar)
returns maximum possible length of a listvar.
value:=ListItem(list,index)
functions as value:=list[index] with the difference that
list may also be a constant value instead of a pointer. This is
very usefull in situations like this where we directly want to
use a list of values:
WriteF(ListItem(['ok!','no mem!','no file!'],error))
this prints an errormessage according to "error". it's similar to:
DEF dummy:PTR TO LONG
dummy:=['ok!','no mem!','no file!']
WriteF(dummy[error])
SetList(listvar,newlen)
manually sets the length of a list. This will only be useful when you read
data into the list by a function other then a list-specific function,
and want to continue using it as a true list.
for list functions that make use of quoted expressions see chapter 11C.
for list linking functions see chapter 9H.
9D. intuition support functions
-------------------------------
wptr:=OpenW(x,y,width,height,IDCMP,wflags,title,screen,sflags,gadgets)
creates a window where wflags are flags for window layout
(like BACKDROP, SIMPLEREFRESH e.d, usually $F) and sflags are
for specifying the type of screen to open on (1=wb,15=custom).
screen must only be valid if sflags=15, else NIL will do.
gadgets may point to a glist structure, which you can easily
create with the Gadget() function, else NIL.
CloseW(wptr)
closes that screen again. Only difference from CloseWindow()
is that it accepts NIL-pointers and sets stdrast back to NIL.
sptr:=OpenS(width,height,depth,sflags,title)
opens a custom screen for you. depth is number of bitplanes (1-6, 1-8 AGA),
sflags is something like 0, or $8000 for hires (add 4 for interlace).
CloseS(sptr)
as CloseW(), now for screens.
nextbuffer:=Gadget(buffer,glist,id,flags,x,y,width,string)
This function creates a list of gadgets, which can then be put in your
window by giving them as an argument to OpenW(), or afterwards with
intuition functions like AddGlist().
buffer is mostly an ARRAY of at least GADGETSIZE bytes to hold all the
structures associated with one gadget, id is any number that may help you
remember which gadget was pressed when an IntuiMessage arrives.
Flags are: 0=normal gadget, 1=boolean gadget, 3=boolean gadget that is
selected. Width is width in pixels, that should be large enough to hold
the string, which will be auto-centered. glist should be NIL for the first
gadget, and glistvar for all others, so E may link all gadgets.
The function returns a pointer to the next buffer (=buffer+GADGETSIZE).
Example for three gadgets:
CONST MAXGADGETS=GADGETSIZE*3
DEF buf[MAXGADGETS]:ARRAY, next, wptr
next:=Gadget(buf,NIL,1,0,10,20,80,'bla') /* the 1st gadget */
next:=Gadget(next,buf,... )
next:=Gadget(next,buf,... ) /* any amount linked 2 1st */
wptr:=OpenW( ...,buf)
See examples like SuperVisor.e for a real-life example.
code:=Mouse()
gives you the current state of all 2 or 3 mouse buttons; left=1,
right=2 and middle=4. If for example code=3 then left and right were
pressed together.
NOTEZ BIEN: this is not a real intuition function, if you want to
know about mouse-events the proper way, you'll have to check the
intuimessages that your window receives. This is the only E
function that directly checks the hardware, and thus only usefull
in demo-like programs.
x:=MouseX(win) and y:=MouseY(win)
enables you to read the mouse coordinates. win is the window
they need to be relative to.
class:=WaitIMessage(window)
This function makes it easier to just wait for a window event. It simply
waits until a intuimessage arrives, and returns the class of the event.
It stores other variables like code and qualifiers as private global
variables, for access with functions described below.
WaitIMessage() represents the following code:
PROC waitimessage(win:PTR TO window)
DEF port,mes:PTR TO intuimessage,class,code,qual,iaddr
port:=win.userport
IF (mes:=GetMsg(port))=NIL
REPEAT
WaitPort(port)
UNTIL (mes:=GetMsg(port))<>NIL
ENDIF
class:=mes.class
code:=mes.code /* stored internally */
qual:=mes.qualifier
iaddr:=mes.iaddress
ReplyMsg(mes)
ENDPROC class
as you see, it gets exactly one message, and does not forget about
multiple messages arriving in one event, if called more than once.
For example, say you opened a window that displays something and just
waits for a closegadget (you specified IDCMP_CLOSEWINDOW only):
WaitIMessage(mywindow)
or, you have a program that waits for more types of events, handles
them in a loop, and ends on a closewindow event:
WHILE (class:=WaitIMessage(win))<>IDCMP_CLOSEWINDOW
/* handle other classes */
ENDWHILE
code:=MsgCode() qual:=MsgQualifier() iaddr:=MsgIaddr()
These all supply you with the private global variables as mentioned
before. the values returned are all defined by the most recent call
to WaitIMessage(). Example:
IF class:=IDCMP_GADGETUP
mygadget:=MsgIaddr()
IF mygadget.userdata=1 THEN /* ... user pressed gadget #1 */
ENDIF
9E. graphics support functions
------------------------------
All graphics support functions that do not explicitly ask for a rastport,
make use of the system-variable 'stdrast'. It is automatically defined by
the last call to OpenW() or OpenS(), and is set to NIL by CloseW() and
CloseS(). Calling these routines while stdrast is still NIL is legal.
stdrast may be manually set by SetStdRast() or stdrast:=myrast
Plot(x,y,colour)
Draws a single dot on your screen/window in one of the colours available.
colour ranges from 0-255, or 0-31 on pre-AGA machines.
Line(x1,y1,x2,y2,colour)
Draws a line
Box(x1,y1,x2,y2,colour)
Draws a box
Colour(foreground,background)
sets the colours for all graphics functions (from the library) that
do not take a colour as argument. This is the colour *register*
(i.e 0-31) and not colour *value*
NOTE: functions that have "colour" as an argument, change the Apen
of stdrast.
TextF(x,y,formatstring,args,...)
exactly the same function as WriteF(), only outputs to some (x,y) on
your stdrast, instead of stdout. See: WriteF() and strings in the language
reference.
oldrast:=SetStdRast(newrast)
changes the output rastport of the e graphics functions
SetTopaz(size)
lets you set the font of the rastport "stdrast" to topaz, just to be sure
that some custom system font of the user won't skrew up your window layout.
size is of course 8 or 9
9F. system support functions
----------------------------
bool:=KickVersion(vers)
Will give TRUE if the kickstart in the machine your program is running
on is equal or higher than vers, else FALSE
mem:=New(n)
This dynamically creates an array (or memory area, if you wish) of
n bytes. Difference with AllocMem() is that it is called automatically
with flags $10000 (i.e cleared mem, any type) and that no calls to
Dispose() are necessary, as it is linked to a memory list that is
automatically de-allocated upon exit of your program.
Dispose(mem)
Frees any mem allocated by New(). You only have to use this function
if you explicitly wish to free memory _during_ your program, as all
is freed at the end anyway.
CleanUp(returnvalue)
Exits the program from any point. It is the replacement for the DOS
call Exit(): never use that one! instead use CleanUp(), which allows
for the deallocation of memory, closing libraries correctly etc.
The return value will be given to dos as returncode.
amount:=FreeStack()
returns the amount of free stack space left. This should always be 1000 or
more. See the chapter 'implementation issues' on how E organizes its
stack. If you don't do heavy recursion, you need not worry about your free
stack space.
bool:=CtrlC()
Returns TRUE if Ctrl-C was pressed since you last checked, else FALSE.
This only works for programs running on a console, i.e. cli-programs.
Example of how these last three functions may be used:
/* calculate faculty from command-line argument */
OPT STACK=100000
PROC main()
DEF num,r
num:=Val(arg,{r})
IF r=0 THEN WriteF('bad args.\n') ELSE WriteF('result: \d\n',fac(num))
ENDPROC
PROC fac(n)
DEF r
IF FreeStack()<1000 OR CtrlC() THEN CleanUp(5) /* xtra check */
IF n=1 THEN r:=1 ELSE r:=fac(n-1)*n
ENDPROC r
Of course, this recursion will hardly run out of stack space, and when it
does, it's halted by FreeStack() so fast you won't have time to press
CtrlC, but it's the idea that counts here.
A definition of fac(n) like:
PROC fac(n) RETURN IF n=1 THEN 1 ELSE fac(n-1)*n
would be less safe.
9G. math and other functions
-------------------
a:=And(b,c) a:=Or(b,c) a:=Not(b)
a:=Eor(b,c)
These work with the usual operations, boolean as well as arithmetic.
Note that for And() and Or() an operator exists.
a:=Mul(b,c) a:=Div(a,b)
Performs the same operation as the '*' and '/' operators, but now in
full 32bit. For speed reasons, normal operations are 16bit*16bit=32bit
and 32bit/16bit=16bit. This is sufficient for nearly all calculations,
and where it's not, you may use Mul() and Div(). NOTE: in the Div
case, a is divided by b, not b by a.
bool:=Odd(x) bool:=Even(x)
Return TRUE or FALSE if some expression is Odd or Even
randnum:=Rnd(max) seed:=RndQ(seed)
Rnd() computes a random number from an internal seed in range 0 .. max-1.
For example, Rnd(1000) returns integer from 0..999
To initialise the internal seed, call Rnd() with a negative value;
the Abs() of that value will be the initial seed.
RndQ() computes a random number "Q"uicker than Rnd() does, but returns
only full range 32bit random numbers. Use the result as the seed for
the next call, and for startseed, use any large value, like $A6F87EC1
absvalue:=Abs(value)
computes the absolute value.
a:=Mod(b,c)
Divides 32bit b by 16bit c and returns 16bit modulo a
x:=Shl(y,num) x:=Shr(y,num)
shifts y num bits to left or right.
a:=Long(adr) a:=Int(adr) a:=Char(adr)
peeks into memory at some address, and returns the value found. This
works with 32, 16 and 8 bit values respectively. Note that the compiler does
not check if 'adr' is valid. These functions are available in E for
those cases where reading and writing in memory with PTR TO <type>
would only make a program more complex or less efficient. You are not
encouraged to use these functions.
PutLong(adr,a) and PutInt(adr,a) PutChar(adr,a)
Pokes value 'a' into memory. See: Long()
9H. string and list linking functions
-------------------------------------
E provides for a set of functions that allows the creation of
linked list with the STRING and LIST datatype, or strings and lists
that were created with String() and List() respectively. As you may
know by now, strings and lists, complex datatypes, are pointers
to their respective data, and have extra fields to a negative offset
of that pointer specifying their current length and maxlength. the
offsets of these fields are PRIVATE. as an addition to those two,
any complex datatype has a 'next' field, which is set to NIL by
default, which may be used to build linked list of strings, for example.
in the following, I will use 'complex' to denote a ptr to a STRING
or LIST, and 'tail' to denote another such pointer, or one that already
has other strings linked to it. 'tail' may also be a NIL pointer,
denoting the end of a linked list.
The following functions may be used:
complex:=Link(complex,tail)
puts the value tail into the 'next' field of complex. returns again complex.
example:
DEF s[10]:STRING, t[10]:STRING
Link(s,t)
creates a linked list like: s --> t --> NIL
tail:=Next(complex)
reads the 'next' field of var complex. this may of course be NIL, or
a complete linked list. Calling Next(NIL) will result in NIL, so it's
safe to call Next when you're not sure if you're at the end of a linked list.
tail:=Forward(complex,num)
same as Next(), only goes forward num links, instead of one, thus:
Next(c) = Forward(c,1)
You may safely call Forward() with a num that is way too large;
Forward will stop if it encounters NIL while searching links, and
will return NIL.
DisposeLink(complex)
same as Dispose(), with two differences: it's only for strings and
lists allocated with String() or List(), and will automatically
de-allocate the tail of complex too. Note that large linked lists
containing strings allocated with String() as well as some allocated
locally or globally with STRING may also be de-allocated this way.
For a good example of how linked lists of strings may be put to
good use in real-life programs, see 'D.e'
+---------------------------------------------------------------+
| 10. LIBRARY FUNCTIONS AND MODULES |
+---------------------------------------------------------------+
10A. built-in library calls
--------------------------
As you may have noticed from previous sections, the piece of code
automatically linked to the start of your code, called the "initialisation code",
always opens the four libraries Intuition, Dos, Graphics and Mathffp,
and because of this, the compiler has all the calls to those five libraries
(including Exec) integrated in the compiler (there are a few hundred of them).
These are up to AmigaDos v2.04, v3.00 should be included by the next version
of Amiga E. To call Open() from the dos library, simply say:
handle:=Open('myfile',OLDFILE)
or AddDisplayInfo() from the graphics library:
AddDisplayInfo(mydispinfo)
it's as simple as that.
10B. interfacing to the amiga system with the 2.04 modules
----------------------------------------------------------
To use any other library than the five in the previous section, you'll
need to resort to modules. Also, if you wish to use some OBJECT or CONST
definition from the Amiga includes as is usual in C or assembler,
you'll need modules. Modules are binary files which may include constant,
object, library and function (code) definitions. The fact that they're
binary has the advantage over ascii (as in C and assembly), that they
need not be compiled over and over again, each time your program is
compiled. The disadvantage is that they cannot be simply be viewed; they
need a utility like ShowModule (see utility.doc) to make their contents
visible. The modules that contain the library definitions (i.e the calls)
are in the root of emodules: (the modules dir in the distribution), the
constant/object definitions are in the subdirectories, structured just
like the originals from Commodore.
MODULE
syntax: MODULE <modulenames>,...
Loads a module. A module is a binary file containing information on libraries,
constants, and sometimes functions. Using modules enables you to use
libraries and functions previously unknown to the compiler.
Now for an example, below is a short version of the source/examples/asldemo.e
source that uses modules to put up a filerequester from the 2.0 Asl.library.
MODULE 'Asl', 'libraries/Asl'
PROC main()
DEF req:PTR TO filerequestr
IF aslbase:=OpenLibrary('asl.library',37)
IF req:=AllocFileRequest()
IF RequestFile(req) THEN WriteF('File: "\s" in "\s"\n',req.file,req.dir)
FreeFileRequest(req)
ENDIF
CloseLibrary(aslbase)
ENDIF
ENDPROC
From the modules 'asl', the compiler takes asl-function definitions like
RequestFile(), and the global variable 'aslbase', which only needs to
be initialised by the programmer. From 'libraries/Asl', it takes
the definition of the filerequestr object, which we use to read the
file the user picked. Well, that wasn't all that hard: did you think
it was that easy to program a filerequester in E?
+---------------------------------------------------------------+
| 11. QUOTED EXPRESSIONS |
+---------------------------------------------------------------+
11A. quoting and scope
----------------------
Quoted expressions start with a backquote. The value of a quoted
expression is not the result from the computation of the expression,
but the address of the code. This result may then be passed on as
a normal variable, or as an argument to certain functions.
example:
myfunc:=`x*x*x
myfunc is now a pointer to a function that computes x3 when evaluated.
These pointers to functions are very different from normal PROCs, and
you should never mix the two up. The biggest differences are that a
quoted expression is just a simple expression, and thus cannot have its
own local variables. In our example, "x" is just a local or global variable.
That's where we have to be cautious:
if we evaluate myfunc somewhat later in the same PROC, x may be local,
but if myfunc is given as parameter to another PROC, and then evaluated,
x needs of course to be global. There's no scope checking on this.
11B. Eval()
-----------
Eval(func)
simply evaluates a quoted expression (exp = Eval(`exp)).
NOTE: because E is a somewhat typeless language, accidentally writing
"Eval(x*x)" instead of "Eval(`x*x)" will go unnoticed by the
compiler, and will give you big runtime problems: the value of x*x
will be used as a pointer to code.
To understand why 'quoted expressions' is a powerful feature think of the
following cases: if you were to perform a set of actions on a set of different
variables, you'd normally write a function, and call that function with
different arguments. But what happens when the element that you want to give
as argument is a piece of code? in traditional languages this would not be
possible, so you would have to 'copy' the blocks of code representing your
function, and put the expression in it. Not in E. say you wanted to write
a program that times the execution time of different expressions. In E you
would simply write:
PROC timing(func,title)
/* do all sorts of things to initialise time */
Eval(func)
/* and the rest */
WriteF('time measured for \s was \d\n',title,t)
ENDPROC
and then call it with:
timing(`x*x*x,'multiplication')
timing(`sizycalc(),'large calculation')
in any other imperative language, you would have to write out
copies of timing() for every call to it, or you would have to
put each expression in a separate function. This is just a simple
example: think about what you could do with data structures (LISTs)
filled with unevaluated code:
drawfuncs:=[`Plot(x,y,c),`Line(x,y,x+10,y+10,c),`Box(x,y,x+20,y+20,c)]
Note that this idea of functions as normal variables/values is not new
in E, quoted expressions are literally from LISP, which also has the
somewhat more powerful so-called Lambda function, which can also be
given as argument to functions; E's quoted expressions can also be
seen as parameterless (or global parameter only) lambda's.
11C. built-in functions
----------------------
MapList(varadr,list,listvar,func)
performs some function on all elements of list and returns all
results in listvar. func must be a quoted expression (see above),
and var (which ranges over the list) must be given by reference. Example:
MapList({x},[1,2,3,4,5],r,`x*x) results r in: [1,4,9,16,25]
ForAll(varadr,list,func)
Returns TRUE if for all elements in the list the function (quoted
expression) evaluates to TRUE, else FALSE. May also be used to perform
a certain function for all elements of a list:
ForAll({x},['one','two','three'],`WriteF('example: \s\n',x))
Exists(varadr,list,func)
As ForAll(), only this one returns TRUE if for any element the function
evaluates to TRUE (<>0). note that ForAll() always evaluates all elements,
but Exists() possibly does not.
Example of how to use these functions in a practical fashion:
we allocate different sizes of memory in one statement, check them
all together at once, and free them all, but still only those that
succeeded. (example is v37+)
PROC main()
LOCAL mem[4]:LIST,x
MapList({x},[200,80,10,2500],mem,`AllocVec(x,0)) /* alloc some */
IF ForAll({x},mem,`x) /* suxxes ? */
WriteF('Yes!\n')
ELSE
WriteF('No!\n')
ENDIF
ForAll({x},mem,`IF x THEN FreeVec(x) ELSE NOP) /* free only those <>NIL */
ENDPROC
Note the absence of iteration in this code. Just try to rewrite this
example in any other language to see why this is special.
+---------------------------------------------------------------+
| 12. FLOATING POINT SUPPORT |
+---------------------------------------------------------------+
12A. using floats and float operator overloading
------------------------------------------------
Overloading the standard operators + * etc with float equivalents is
possible starting from v2.0 of Amiga E, but I've removed the main
documentation on it because it is likely that the float-concept in E
will change as of v2.2 or later: that version may allow for 68881 inline
code generation next to normal FFP routines in a transparent fashion.
If you really want to use floats with v2.1b, you are advised to use the
SpXxx() built-in routines from the mathffp.library.
Example:
x:=SpMul(y,0.013483)
Be aware that when v2.5 comes out, your sources may need to be
changed (for the better!).
12B. float expressions and conversion
-------------------------------------
as 12A.
+---------------------------------------------------------------+
| 13. EXCEPTION HANDLING |
+---------------------------------------------------------------+
13A. defining exception handlers (HANDLE/EXCEPT)
------------------------------------------------
The exception mechanism in E is basically the same as in ADA; it
provides for flexible reaction on errors in your program and
complex resource management. NOTE: the term 'exception' in E has
very little to do with exceptions caused directly by 680x0 processors.
An exception handler is a piece of program code that will be invoked
when runtime errors occur, such as windows that fail to open or
memory that is not available. You, or the runtime system itself,
may signal that something is wrong (this is called "raising an
exception"), and then the runtime-system will try and find the
appropriate exception handler. I say "appropriate" because a program
can have more than one exception handler, on all levels of a program.
A normal function definition may (as we all know) look like this:
PROC bla()
/* ... */
ENDPROC
a function with an exception handler looks like this:
PROC bla() HANDLE
/* ... */
EXCEPT
/* ... */
ENDPROC
The block between PROC and EXCEPT is executed as normal, and if no
exception occur, the block between EXCEPT and ENDPROC is skipped, and
the procedure is left at ENDPROC. If an exception is raised, either
in the PROC part, or in any function that is called in this block,
an exception handler is invoked.
13B. using the Raise() function
-------------------------------
There are many ways to actually "raise" an exception, the simplest
is through the function Raise():
Raise(exceptionID)
the exception ID is simply a constant that defines the type of
exception, and is used by handlers to determine what went wrong.
Example:
ENUM NOMEM,NOFILE /* and others */
PROC bla() HANDLE
DEF mem
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
myfunc()
EXCEPT
SELECT exception
CASE NOMEM
WriteF('No memory!\n')
/* ... and others */
ENDSELECT
ENDPROC
PROC myfunc()
DEF mem
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
ENDPROC
The "exception" variable in the handler always contains the value of
the argument to the Raise() call that invoked it.
In both New() cases, the Raise() function invokes the handler of
function bla(), and then exits it correctly to the caller of bla().
If myfunc() had its own exception-handler, that one would be invoked
for the New() call in myfunc(). The scope of a handler is from the start
of the PROC in which it is defined until the EXCEPT keyword, including
all calls made from there.
This has three consequences:
A. handlers are organised in a recursive fashion, and which handler is
actually invoked is dependant on which function calls which at runtime;
B. if an exception is raised within a handler, the handler of a lower
level is invoked. This characteristic of handlers may be used
to implement complex recursive resource allocation schemes with
great ease, as we'll see shortly.
C. If an exception is raised on a level where no lower-level handler
is available (or in a program that hasn't got any handlers at all),
the program is terminated. (i.e: Raise(x) has the same effect as
CleanUp(0))
13C. defining exceptions for built-in functions (RAISE/IF)
---------------------------------------------------------
With exceptions like before, we have made a major gain over the
old way of defining our own "error()" function, but still it is
a lot of typing to have to check for NIL with every call to New().
The E exception handling system allows for definition of exceptions
for all E functions (like New(), OpenW() etc.), and for all Library
functions (OpenLibrary(), AllocMem() etc.), even for those
included by modules. Syntax:
RAISE <exceptionId> IF <func> <comp> <value> , ...
the part after RAISE may be repeated with a ",".
Example:
RAISE NOMEM IF New()=NIL,
NOLIBRARY IF OpenLibrary()=NIL
the first line says something like: "whenever a call to New() results
in NIL, automatically raise the NOMEM exception".
<comp> may be any of = <> > < >= <=
After this definition, we may write all through our programs:
mem:=New(size)
without having to write:
IF mem=NIL THEN Raise(NOMEM)
Note that the only difference is that "mem" never gets any value
if the runtime system invokes the handler: code is generated for
every call to New() to check directly after New() returns and call
Raise() when necessary.
We'll now be implementing a small example that would be complex to solve
without exception handling: we call a function recursively, and in each
we allocate a resource (in this case memory), which we allocate before,
and release after the recursive call. What happens when somewhere high
in the recursion a severe error occurs, and we have to leave the program?
right: we would (in a conventional language) be unable to free all the
resources lower in the recursion while leaving the program, because all
pointers to those memory areas are stored in unreachable local variables.
In E, we can simply raise an exception, and from the end of the handler
again raise an exception, thus recursively calling all handlers and
releasing all resources. Example:
CONST SIZE=100000
ENUM NOMEM /* ,... */
RAISE NOMEM IF AllocMem()=NIL
PROC main()
alloc()
ENDPROC
PROC alloc() HANDLE
DEF mem
mem:=AllocMem(SIZE,0) /* see how many blocks we can get */
alloc() /* do recursion */
FreeMem(mem,SIZE) /* we'll never get here */
EXCEPT
IF mem THEN FreeMem(mem,SIZE)
Raise(exception) /* recursively call all handlers */
ENDPROC
This is of course a simulation of a natural programming problem that
is usually far more complex, and thus the need for exception handling
becomes far more obvious. For a real-life example program whose error
handling would have become very difficult without exception
handlers, see the 'D.e' utility source.
13D. use of exception-ID's
--------------------------
In real life an exception-ID is ofcourse a normal 32-bit value,
and you may pass just about anything to an exception handler: for
example, some use it to pass error-description strings
Raise('Could not open "gadtools.library"!')
However, if you want to use exceptions in expandabele fashion and you
want to be able to use future modules that raise exceptions not defined
by your program, follow the following guidelines:
- Use and define ID 0 as "no error" (i.e. normal termination)
- For exceptions specific to your program, use the ID's 1-10000.
Define these in the usual fashion with ENUM:
ENUM OK,NOMEM,NOFILE,...
(OK will be 0, and others will be 1+)
- ID's 12336 to 2054847098 (these are all identifiers
consisting of upper/lowercase letters and digits of lenght 2,3 or 4
enclosed in "") are reserved as common exceptions. A common exception
is an exception that need not need be defined in your program, and that
may be used by implementors of modules (with functions in them) to
raise exceptions: for example, if you design a set of procedures that
perform a certain task, you may want to raise exceptions. As you would
want to use those functions in various programs, it would be
unpractical to have to coordinate the ID's with the main program,
furthermore, if you use more than one set of functions (in a module,
in the future) and every module would have a different ID for
'no memory!', things could get out of hand.
This is where common exceptions come in: the common out-of-memory
ID is "MEM" (including the quotes): any implementor can now simply
Raise("MEM")
from all different procedures, and the programmer that uses the module
only needs to suply an exception handler that understands "MEM"
future modules that contain sets of functions will specify what
exception a certain procedure may raise, and if these overlap
with the ID's of other procedures, the task of the programmer
that has to deal with the exceptions will be greatly simplyfied.
examples:
(system)
"MEM" out of memory
"FLOW" (nearly) stack overflow
"^C" Control-C break
"ARGS" bad args
(exec/libraries)
"SIG" could not allocate signal
"PORT" could not create messageport
"LIB" library not available
"ASL" no asl.library
"UTIL" no utility.library
"LOC" no locale.library
"REQ" no req.library
"RT" no reqtools.library
"GT" no gadtools.library (similar for others)
(intuition/gadtools/asl)
"WIN" failed to open window
"SCR" failed to open screen
"REQ" could not open requester
"FREQ" could not open filerequester
"GAD" could not create gadget
"MENU" could not create menu(s)
(dos)
"OPEN" could not open a file / file does not exist
"OUT" problems while reading
"IN" problems while writing
"EOF" unexpected end of file
"FORM" input format error
The general tendancy is uppercase for general system
exceptions, and lowercase (and mixed) for specific modules.
- all others (including all negative ID's) remain reserved.
+---------------------------------------------------------------+
| 14. OO PROGRAMMING |
+---------------------------------------------------------------+
As this hasn't been implemented yet, it's not documented either.
+---------------------------------------------------------------+
| 15. INLINE ASSEMBLY |
+---------------------------------------------------------------+
15A. identifier sharing
-----------------------
As you've probably guessed from the example in chapter 5D, assembly
instructions may be freely mixed with E code. The big secret is, that
a complete assembler has been built in to the compiler.
Apart from normal assembly addressing modes, you may use the following
identifiers from E:
mylabel:
LEA mylabel(PC),A1 /* labels */
DEF a /* variables */
MOVE.L (A0)+,a /* note that <var> is <offset>(A4) (or A5) */
MOVE.L dosbase,A6 /* library call identifiers */
JSR Output(A6)
MOVEQ #TRUE,D0 /* constants */
15B. the inline assembler compared to a macro assembler
-------------------------------------------------------
The inline assembler differs somewhat from your average macro-assembler,
and this is caused mainly by the fact that it is an extension to E,
and thus it obeys E-syntax. Main differences:
- comments are with /* */ and not with ";", they have a different meaning.
- keywords and registers are in uppercase, everything is case sensitive
- no macros and other luxury assembler stuff (well, there's the complete
E language to make up for that ...)
- You should be aware that registers A4/A5 may not be trashed by inline
assembly code, as these are used by E code.
- no support for LARGE model/reloc-hunks in assembly _YET_.
This means practically that you have to use (PC)-relative addressing
for now.
15C. ways using binary data (INCBIN/CHAR..)
-------------------------------------------
INCBIN
syntax: INCBIN <filename>
includes a binary file at the exact spot of the statement, should
therefore be separate from the code. Example:
mytab: INCBIN 'df1:data/blabla.bin'
LONG, INT, CHAR
syntax: LONG <values>,...
INT <values>,...
CHAR <values>,...
Allows you to place binary data directly in your program. Functions much
like DC.x in assembly. Note that the CHAR statement also takes strings,
and will always be aligned to an even word-boundary. Example:
mydata: LONG 1,2; CHAR 3,4,'hi folks!',0,1
15D. OPT ASM
------------
OPT ASM is discussed also in chapter 16A. It allows you to operate
'EC' as an assembler. There's no good reason to use EC over some
macro-assembler, except that it is significantly faster than for example
A68k, equals DevPac and loses from AsmOne (sob 8-{). You will also have
a hard time trying to squeeze your disks of old seka-sources through EC,
because of the differences as described in chapter 15B. If you want to write
assembly programs with EC, and want to keep your sources compatible with
other assemblers, simply precede all E-specific elements with a ";",
EC will use them, and any other assembler will see them as a comment.
Example:
; OPT ASM
start: MOVEQ #1,D0 ; /* do something silly */
RTS ; /* and exit */
this will be assembled by any assembler, including EC
+---------------------------------------------------------------+
| 16. IMPLEMENTATION ISSUES |
+---------------------------------------------------------------+
16A. the OPT keyword
--------------------
OPT, LARGE, STACK, ASM, NOWARN, DIR, OSVERSION
syntax: OPT <options>,...
allows you to change some compiler settings:
LARGE Sets code and data model to large. Default is small;
the compiler generates 100% pc-relative code, with a
max-size of 32k. With LARGE, there are no such limits,
and reloc-hunks are generated. See -l
STACK=x Set stacksize to x bytes yourself. Only if you know what
you are doing. Normally the compiler makes a very good
guess itself at the required stack space.
ASM Set the compiler to assembly mode. From there on, only
assembly instructions are allowed, and no initialisation
code is generated. See: chapter inline assembly
NOWARN Shut down warnings. The compiler will warn you if it
*thinks* your program is incorrect, but still syntactically
ok. See -n
DIR=moduledir Sets the directory where the compiler searches for modules.
default='emodules:'
OSVERSION=vers Default=33 (v1.2). Sets the minimum version of the kickstart
(like 37 for v2.04) your program runs on. That way, your
program simply fails while the dos.library is being opened
in the initialisation code when running on an older machine.
However, checking the version yourself and giving an
appropriate error-message is more helpful for the user.
example:
OPT STACK=20000,NOWARN,DIR='df1:modules',OSVERSION=39
16B. small/large model
----------------------
Amiga E lets you choose between SMALL and LARGE code/data model.
Note that most of the programs you'll write (especially if you just
started with E) will fit into 32k when compiled: you won't have to
bother setting some code-generation model. You'll recognise the
need for LARGE model as soon as EC starts complaining that it can't
squeeze your code into 32k anymore. To compile a source with LARGE model:
1> ec -l sizy.e
or better yet, put the statement
OPT LARGE
in your code.
16C. stack organisation
-----------------------
To store all local and global variables, the run-time system of an
executable generated by Amiga E allocates a chunk of memory,
from which it takes some fixed part to store all global variables.
The rest will be dynamically used as functions get called.
as a function is called in E, space on the stack is reserved
to store all local data, which is released upon exit of the function.
That is why having large arrays of local data can be dangerous when
used recursively: all data of previous calls to the same function
still resides on the stack and eats up large parts of the free stack
space. However, if PROC's are called in a linear fashion, there's
no way the stack will overflow.
Example:
global data: 10k (arrays e.d)
local data PROC #1: 1k
local data PROC #1: 3k
the runtime system always reserves an extra 10k over this for normal
recursion (for example with small local-arrays) and additional buffers/
system spaces, thus will allocate a total of 24k stack space
16D. hardcoded limits
---------------------
Note these signs: (+-) just about, depends on situation,
(n.l.) no clear limit, but this seems reasonable.
--------------------------------------------------------------------------
OBJECT/ITEM SIZE/AMOUNT/MAX
--------------------------------------------------------------------------
value datatype CHAR 0 .. 255
value datatype INT -32 k .. +32 k
value datatype LONG/PTR -2 gig .. +2 gig
identifierlength 100 bytes (n.l.)
length of one source line 2000 lexical tokens (+-)
source length 2 gig (theoretically)
constant lists few hundred elements (+-)
constant strings 1000 chars (n.l.)
max. nesting depth of loops (IF, FOR etc.) 500 deep
max. nesting depth of comments infinite
#of local variables per procedure 8000
#of global variables 7500
#of arguments to own functions 8000 (together with locals)
#of arguments to E-varargs functions (WriteF()) 64
one object (allocated local/global or dyn.) 8 k
one array, list or string (local or global) 32 k
one string (dynamically) 32 k
one list (dynamically) 128 k
one array (dynamically) 2 gig
local data per procedure 250 meg
global data 250 meg
code size of one procedure 32 k
code size of executable 32 k SMALL, 2 gig LARGE model
current practical limit (may extend in future) 2-5 meg
buffersize of generated code and identifiers relative to source
buffersize of labels/branches and intermediate independently (re)allocated
16E. error messages, warnings and the unreferenced check
--------------------------------------------------------
Sometimes, when compiling your source with EC, you get a message
of the sort UNREFERENCED: <ident>, <ident>, ...
This is the case when you have declared variables, functions or labels,
but did not use them. This is an extra service rendered to you by the
compiler to help you find out about those hard to find errors.
There are several warnings that the compiler issues to notify you that
something might be wrong, but is not really an error.
- "A4/A5 used in inline assembly"
This is the warning you'll get if you use registers A4 or A5 in your
assembly code. The reason for this is that those registers are used
internally by E to address the global and local variables respectively.
Of course there might be a good reason to use these, like doing
a MOVEM.L A4/A5,-(A7) before a large part of inline assembly code
- "keep an eye on your stacksize"
- "stack is definitely too small"
Both these may be issued when you use OPT STACK=<size>. The compiler
will simply match your <size> against its own estimate (see chapter 16C),
and issue the former warning if it thinks it's ok but a bit on the small
side, and the latter if it's probably too small.
- 'suspicious use of "=" in void expressions'
This warning is issued if you write expressions like 'a=1' as a
statement. One reason for this is the fact that a comparison doesn't
make much sense as a statement, but the main reason is that it could be
an often occurring typo for 'a:=1'. Forgetting those ":" may be hard to
find, and it may have disastrous consequences.
Errors.
- 'syntax error'
Most common error. This error is issued either when no other
error is appropriate or your way of ordering code in your sources
is too abnormal.
- 'unknown keyword/const'
You have used an identifier in uppercase (like "IF" or "TRUE"), and
the compiler could not find a definition for it. Causes:
* mispelled keyword
* you used a constant, but forgot to define it in a CONST statement
* you forgot to specify the module where your constant is defined
- '":=" expected'
You have written a FOR statement or an assignment, and put something
other than ":=" in its place.
- 'unexpected characters in line'
You used characters that have no syntactic meaning in E outside of
a string. examples: "@!&\~"
- 'label expected'
At some places, for example after the PROC or JUMP keyword,
a label identifier is required. You wrote something else.
- '"," expected'
In specifying a list of items (for example a parameter list)
you wrote something else instead of a comma.
- 'variable expected'
This construction requires a variable, example:
FOR <var>:= ... etc.
- 'value does not fit in 32 bit'
In specifying a constant value (see chapter 2A-2E) you wrote too
large a number, examples: $FFFFFFFFF, "abcdef".
Also occurs when you define a SET of more than 32 elements.
- 'missing apostrophe/quote'
You forgot the ' at the other end of a string.
- 'incoherent program structure'
* you started a new PROC before ending the last one
* you don't nest your loops properly, for example:
FOR
IF
ENDFOR
ENDIF
- 'illegal command-line option'
In specifying 'EC -opt source' you wrote something for '-opt'
that is not a legal option to EC.
- 'division and multiplication 16bit only'
The compiler detected that you were about to use 32bits
for * or /. This would not have the desired result at runtime.
See Mul() and Div().
- 'superfluous items in expression/statement'
After the compiler already compiled your statement, it still found
lexical tokens instead of an end of line. You probably forgot
the <lf> or ";" to separate two statements.
- 'procedure "main" not available'
Your program does not include a main procedure !
- 'double declaration of label'
You declared a label twice, for example:
label:
PROC label()
- 'unsafe use of "*" or "/"'
This again has to do with 16bit instead of 32bit * and /.
See 'division and multiplication 16bit only'.
- "reading sourcefile didn't succeed"
Check your source spec. that you gave with 'ec mysource'
make sure the file ends in '.e', and your command line doesn't.
- "writing executable didn't succeed"
Trying to write the generated code as an executable caused a dos
error. For example, the executable that did already exist could
not be overwritten.
- 'no args'
"USAGE: ec [-opts] <sourcecodefilename> (`.e' is added)"
You get this by just typing 'ec' without any arguments.
- 'unknown/illegal addressing mode'
This error is reported only by the inline assembler. Possible causes are:
* you used some addressing mode that does not exist on the 68000
* the addressing mode exists, but not for this instruction.
not all assembly instructions support all combinations of
effective addresses for source and destination.
- 'unmatched parentheses'
Your statement has more "(" than ")" or the other way around
- 'double declaration'
One identifier is used in two or more declarations.
- 'unknown identifier'
An identifier is not used in any declaration; it is unknown.
You probably forgot to put it in a DEF statement.
- 'incorrect #of args or use of ()'
* You forgot to put "(" or ")" at the right spot
* you supplied the incorrect #of arguments to some function
- 'unknown e/library function'
You wrote an identifier with the first character in uppercase, and
the second in lowercase, but the compiler could not find a definition.
Possible causes:
* Misspelled name of function
* You forgot to include the module that defines this library call.
- 'illegal function call'
Rarely occurs. You get this one if you try to construct weird
function calls like nested WriteF()'s. Example:
WriteF(WriteF('hi!'))
- 'unknown format code following "\"'
You specified a format code in a string which is illegal.
See chapter 2F for a listing of format codes
- '/* not properly nested comment structure */'
The #of '/*' is unequal to the #of '*/', or is placed in a funny order.
- 'could not load binary'
<filespec> in INCBIN <filespec> could not be read.
- '"}" expected'
You started an expression with "{<var>" , but forgot the "}"
- 'immediate value expected'
Some constructions require an immediate value instead of an expression.
Example:
DEF s[x*y]:STRING /* wrong: only something like s[100]:STRING is legal */
- 'incorrect size of value'
You specified an unacceptably large (or small) value for some construction.
Examples:
DEF s[-1]:STRING, t[1000000]:STRING /* needs to be 0..32000 */
MOVEQ #1000,D2 /* needs to be -128..127 */
- 'no e code allowed in assembly modus'
You wish to operate the compiler as an assembler by writing 'OPT ASM',
but, by accident, wrote some E code.
- 'illegal/inappropriate type'
At someplace where a <type> spec. was needed, you wrote something
inappropriate. Examples:
DEF a:PTR TO ARRAY /* no such type */
[1,2,3]:STRING
- '"]" expected'
You started with "[", but never ended with "]"
- 'statement out of local/global scope'
A breakpoint of scope is the first PROC statement. before that,
only global definitions (DEF,CONST,MODULE etc.) are allowed, and no code.
In the second part, only code and function definitions are legal, no
global definitions.
- 'could not read module correctly'
A dos error occurred while trying to read a module from a MODULE
statement. Causes:
* emodules: was not assigned properly
* module name was misspelled, or did not exist in the first place
* you wrote MODULE 'bla.m' instead of MODULE 'bla'
- 'workspace full!'
Rarely occurs. If it does, you'll need the '-m' option to manually
force EC to make a bigger estimate on the needed amount of memory.
Try compiling with -m2, then -m3 until the error disappears.
You'll probably be writing huge applications with giant amounts
of data just to even possibly get this error.
- 'not enough memory while (re-)allocating'
Just like that. Possible solutions:
1. You were running other programs in multitasking. Leave them and try again.
2. You were low on memory anyway and your memory was fragmented.
Try rebooting.
3. None of 1-2. Buy a memory expansion (ahum).
- 'incorrect object definition'
You were being silly while writing the definitions between OBJECT and
ENDOBJECT. See chapter 8F to find out how to do it right.
- 'illegal use of/reference to object'
If you use expressions like ptr.member, member needs to be a legal
member of the object ptr is pointing to.
- 'incomplete if-then-else expression'
If you use IF as an operator (see chapter 4E), then an ELSE part
needs to be present: an expression with an IF in it always needs to
return a value, while a statement with an IF in it can just 'do nothing'
if no ELSE part is present.
- 'unknown object identifier'
You used an identifier that was recognised by the compiler as being
part of some object, but you forgot to declare it. Causes:
* misspelled name
* missing module
* the identifier in the module is spelled not like you expected
from the RKRM's. Check with ShowModule.
Note that amiga-system-objects inherit from assembly identifiers,
not from C. Second: identifiers obey E-syntax.
- 'double declaration of object identifier'
One identifier used in two object definitions
- 'reference(s) out of 32k range: switch to LARGE model'
Your program is growing larger than 32k. Simply put 'OPT LARGE'
in your source and code on. See Chapter 16B.
- 'reference(s) out of 256 byte range'
You probably wrote BRA.S or Bcc.S over too great a distance.
- 'too sizy expression'
You used a list [], possibly recursive [[]], that is too sizy.
- 'incomplete exception handler definition'
You probably used EXCEPT without HANDLE, or the other way round
see chapter 13 on exception handling.
16F. compiler buffer organisation and allocation
------------------------------------------------
When you get the error 'workspace full' (very unlikely), or want
to know what really happens when your program is compiled, it's useful
to know how EC organises its buffers.
A compiler, and in this case EC needs buffers to keep track of all sorts
of things, like identifiers etc., and it needs a buffer to keep the
generated code in. EC doesn't know how big these buffers need to be.
for some buffers, like the one for storing constants, this is no
problem: if the buffer is full while compiling, EC just allocates a
new piece of memory and continues. Other buffers, like the one for
the generated code, need to be a continuous block of memory that doesn't
move while compiling: EC needs to make a pretty good estimate of
this buffersize to be able to compile small and large sources alike.
To do this, EC computes the needed memory relative to the size of
your source code, and adds a nice amount to it. This way, in 99% of the
cases, EC will have allocated enough memory to compile just about any
source, in other cases, you'll get the error and have to specify more
memory with the '-m' option.
Experiment with different types and sizes of example-sources in combination
with the '-b' option to see how this works in practice.
16G. a small history
--------------------
E is not 'just another language': it was carefully and gradually designed by
the author of the compiler because he was not too happy with existing
programming languages, and specifically not the sluggish-code-generating
and slow compilers that were written for them. Amiga E had as primary
goal to be used as language for the author to program his amiga programs
in, and he has succeeded in doing so by far. E was developed intensively
over the course of 1.5 years and was certainly not the first compiler
written by the author: some of you may remember the DEX compiler.
This one was slow and unpowerful and is hardly something that can be
compared to a compiler like Amiga E, but certainly gave the author some
useful experience to be able to make Amiga E what it is today.
DEX programmers will notice that it is very easy to convert their old
DEX sources to E, and continue developing with 10x the power at 20x the
speed. A funny thing about DEX and E is that the development of the
two compilers did overlap: while DEX was done, E was halfway v1.6.
Because E was already much better back then, E libraries/examples and
code were transferred to DEX by popular demand, so the predecessor
inherited features from its successor.
The author also wrote numerous other compilers and interpreters, some
of which were never distributed in any way.
Amiga E is a product that will continue to be developed towards the ultimate
language / amiga development system:
- by implementing those missing parts in the language definition
* Object Orientedness
* better float concept
- by making compiler specific enhancements
* possible 020/030/881 code generation
* optimizing the compilation-process, thus possibly doubling the
line/minute figures as in compiler.doc
* enabling the user to compile own code to modules, and thus develop large
applications in a more modular fashion
- by adding valuable elements to the distribution
* an integrated editor ?
* source-level debugger ?
* CASE tools, for example
- by fixing bugs (what bugs!?!) 8*-)
The END! phew! Have fun with E!